﻿#include "lua_3rd.h"
#include "lptree.h"
#include "pb.h"
#include "luasql.h"
#include "string.h"
#include "stdlib.h"

#ifdef __linux__
#include "zlib.h"

static int api_string_zlib_encode(lua_State* L){
	size_t src_len = 0;
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* src = lua_tolstring(L, 1, &src_len);

	uLongf dst_len = compressBound(src_len);
	char* dst = (char*)malloc(dst_len);
	int ret = compress((Bytef*)dst, &dst_len, (const Bytef*)src, src_len);
	if (Z_OK != ret){
		free(dst);
		return 0;
	}
	
	lua_pushlstring(L, dst, dst_len);
	free(dst);
	return 1; 
}

static int api_string_zlib_decode(lua_State* L){
	size_t src_len = 0;
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* src = lua_tolstring(L, 1, &src_len);

	size_t dst_len = src_len * 20;
	char* dst = (char*)malloc(dst_len);

	while (1) {
		int nre = uncompress((Bytef*)dst, &dst_len, (const Bytef*)src, src_len);
		if (Z_BUF_ERROR == nre) {
			dst_len *= 2;
			dst = (char*)realloc(dst, dst_len);
			continue;
		}
		else if (Z_OK == nre) {
			break;
		}
		else {
			free(dst);
			return 0;
		}
	}

	lua_pushlstring(L, dst, dst_len);
	free(dst);
	return 1; 
}

static int api_string_zlib_adler32(lua_State* L){
	size_t src_len = 0;
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* src = lua_tolstring(L, 1, &src_len);

	int adl32 = adler32(1, (const Bytef*)src, src_len);
	lua_pushinteger(L, adl32);
	return 1;
}
#endif

static int api_string_split(lua_State* L)
{
	size_t dlen = 0;
	size_t splen = 0;
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* data = lua_tolstring(L, 1, &dlen);
	luaL_checktype(L, 2, LUA_TSTRING);
	const char* sp = lua_tolstring(L, 2, &splen);

	lua_newtable(L);
	char* p = NULL;
	char* s = (char*)data;
	int i = 1;
	while ((p = (char*)strstr(s, sp)) != NULL)
	{
		if (p != s)
		{
			*p = 0x0;
			lua_pushnumber(L, i++);
			lua_pushlstring(L, s, p - s);
			lua_settable(L, -3);
			*p = *sp;
		}
		s = p + splen;
	}
	if (s - data < (int)dlen)
	{
		lua_pushnumber(L, i++);
		lua_pushstring(L, s);
		lua_settable(L, -3);
	}
	return 1;
}


LUALIB_API int register_common_func(lua_State* L) {
	lua_getglobal(L, "string");

	lua_pushstring(L, "split");
	lua_pushcfunction(L, api_string_split);
	lua_settable(L, -3);

#ifdef __linux__
	lua_pushstring(L, "zlib_encode");
	lua_pushcfunction(L, api_string_zlib_encode);
	lua_settable(L, -3);

	lua_pushstring(L, "zlib_decode");
	lua_pushcfunction(L, api_string_zlib_decode);
	lua_settable(L, -3);

	lua_pushstring(L, "zlib_adler32");
	lua_pushcfunction(L, api_string_zlib_adler32);
	lua_settable(L, -3);
#endif

	lua_remove(L, -1);

	return 0;
}

static const luaL_Reg loadedlibs[] = {
    {"lpeg", luaopen_lpeg},
    {"sproto.core", luaopen_sproto_core},

    {"crypt", luaopen_client_crypt},

    {"pb", luaopen_pb},
    {"pb.unsafe", luaopen_pb_unsafe},
    {"pb.io", luaopen_pb_io},
    {"pb.buffer", luaopen_pb_buffer},
    {"pb.slice", luaopen_pb_slice},
    {"pb.conv", luaopen_pb_conv},
    {NULL, NULL}
};


LUALIB_API void luaL_open_3rd_lib(lua_State* L) {
    const luaL_Reg* lib;
    /* "require" functions from 'loadedlibs' and set results to global table */
    for (lib = loadedlibs; lib->func; lib++) {
        luaL_requiref(L, lib->name, lib->func, 1);
        lua_pop(L, 1);  /* remove lib */
    }
}

static const luaL_Reg loadedlibs_server[] = {
    {"luasql.sqlite3", luaopen_luasql_sqlite3},
    {NULL, NULL}
};


LUALIB_API void luaL_open_3rd_server_lib(lua_State* L) {
    const luaL_Reg* lib;
    /* "require" functions from 'loadedlibs' and set results to global table */
    for (lib = loadedlibs_server; lib->func; lib++) {
        luaL_requiref(L, lib->name, lib->func, 1);
        lua_pop(L, 1);  /* remove lib */
    }
}